package com.zlx.mall.basic.util.crypto;

import lombok.extern.slf4j.Slf4j;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.Security;
import java.util.Arrays;

/**
 * @author qingke
 **/
@Slf4j
public final class AESUtil {
    public static final String ALGORITHM = "AES";
    public static final String TRANSFORMATION = "AES/ECB/PKCS5Padding";
    public static final String PROVIDER = "BC";

    static {
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
    }

    private AESUtil() {

    }

    /**
     * The method that will generate a random {@link SecretKey}.
     *
     * @return The key generated.
     */
    public static SecretKey generateKey() {
        try {
            KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM);
            keyGenerator.init(128);
            return keyGenerator.generateKey();
        } catch (Exception e) {
            throw new IllegalStateException("generateKey exception", e);
        }
    }

    /**
     * Creates a new {@link SecretKey} based on a password.
     *
     * @param password The password that will be the {@link SecretKey}.
     * @return The key.
     */
    public static SecretKey createKey(String password) {
        try {
            byte[] key = password.getBytes(StandardCharsets.UTF_8);
            MessageDigest digest = MessageDigest.getInstance("SHA-1");
            key = digest.digest(key);
            // use only first 128 bit(8|16|24)
            key = Arrays.copyOf(key, 16);

            return new SecretKeySpec(key, ALGORITHM);

        } catch (Exception e) {
            throw new IllegalStateException("createKey exception", e);
        }
    }

    public static SecretKey decodeKey(String key) {
        return decodeKey(key.getBytes(StandardCharsets.UTF_8));
    }

    public static SecretKey decodeKey(byte[] key) {
        return new SecretKeySpec(key, ALGORITHM);
    }

    /**
     * The method that will encrypt data.
     *
     * @param secretKey The key used to encrypt the data.
     * @param data      The data to encrypt.
     * @return The encrypted data.
     */
    public static byte[] encrypt(SecretKey secretKey, byte[] data) {
        try {
            Cipher cipher = Cipher.getInstance(TRANSFORMATION, PROVIDER);
            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
            return cipher.doFinal(data);
        } catch (Exception e) {
            throw new IllegalStateException("encrypt exception", e);
        }
    }

    /**
     * The method that will encrypt data.
     *
     * @param secretKey The key used to encrypt the data.
     * @param data      The data to encrypt.
     * @return The encrypted data.
     */
    public static String encryptToBase64(SecretKey secretKey, byte[] data) {
        return MoreBase64Utils.encodeToString(encrypt(secretKey, data));
    }

    /**
     * The method that will decrypt a piece of encrypted data.
     *
     * @param secretKey The key used to decrypt encrypted data.
     * @param encrypted The encrypted data.
     * @return The decrypted data.
     */
    public static byte[] decrypt(SecretKey secretKey, byte[] encrypted) {
        try {
            Cipher cipher = Cipher.getInstance(TRANSFORMATION, PROVIDER);
            cipher.init(Cipher.DECRYPT_MODE, secretKey);
            return cipher.doFinal(encrypted);
        } catch (Exception e) {
            throw new IllegalStateException("decrypt exception", e);
        }
    }

    /**
     * The method that will decrypt a piece of encrypted data.
     *
     * @param secretKey The key used to decrypt encrypted data.
     * @param base64    The encrypted data.
     * @return The decrypted data.
     */
    public static byte[] decryptFromBase64(SecretKey secretKey, String base64) {
        return decrypt(secretKey, MoreBase64Utils.decodeFromMimeString(base64));
    }



}
